#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "html.h"
#include "utils.h"

#define LENGTH(x) (sizeof (x) / sizeof (*x))

char *line = NULL;
unsigned linenum = 0;
size_t linelen;
char *params[9] = { 0 };
int nparams;

int inparagraph = 0;

struct {
	char *name;
	int(*fun)(void);
} tokens[] = { //TODO: make the recognition not dependent on length+order
	{ ".img", spitimg },
	{ ".ref", spithref },
	{ ".br", spitlinebreak },
	{ ".ce", codeend },
	{ ".cs", codestart },
	{ ".cw", spitmonospace },
	{ ".hr", spithorizontalrule },
	{ ".pp", newparagraph },
	{ ".sh", subheading },
	{ ".b", spitbold },
	{ ".i", spititalic },
	//{ "", newparagraph },
};

char *
nextword(char *p)
{
	// find the end of current word
	for (;; p++) {
		if (*p == '\0') {
			return NULL;
		} else if (isspace(*p)) {
			//TODO: is this correct?
			*p = '\0';
			p++;
			break;
		}
	}

	// find the first letter of next word
	for (;; p++) {
		if (*p == '\0') {
			return NULL;
		} else if (!isspace(*p)) {
			break;
		}
	}

	return p;
}

int
paramize(void)
{
	char *p = line;
	char *w = line;

	nparams = 0;
	while ((w = nextword(p)) != NULL) {
		params[nparams++] = w;
		//puts(w);
		p = w;
	}

	return 0;
}

int
spit(void)
{
	// TODO: should we always spit the original?
	return fputs(line, stdout);
}

int
tokenp(void)
{
	int i;

	// implicit new paragraph via an empty line is a special case
	if (line[0] == '\n') {
		newparagraph();
		return 0;
	}

	for (i = 0; i < LENGTH(tokens); i++) {
		const char * t = tokens[i].name;
		size_t len = strlen(t);
		if (!strncmp(line, t, len) && isspace(line[len])) {
			paramize();
			tokens[i].fun();
			return 1;
		}
	}

	spit();

	return 0;
}

int
main(int argc, char *argv[])
{
	FILE *stream = stdin;
	size_t len = 0;
	ssize_t nread;

	spitheader();

	while ((nread = getline(&line, &len, stream)) != -1) {
		linenum++;
		tokenp();
	}

	free(line);

	if (inparagraph) {
		puts("</p>");
	}

	spitfooter();

	if (nerrors > 0) {
		fprintf(stderr, "Generation finished with %d error(s).\n", nerrors);
		return 1;
	}

	return 0;
}
